home *** CD-ROM | disk | FTP | other *** search
Wrap
/* File: ShimSerialUART.c Contains: Routines for controlling an 8250, 16450, or 16550 UART Version: xxx put version here xxx Copyright: © 1994-1998 by Apple Computer, Inc., all rights reserved. */ #include <Gestalt.h> #include <LowMem.h> #include <Memory.h> #include "ShimSerialInternal.h" #include "SerialBox.h" #define DebugMessage(x) SysDebugStr(x) //#define DebugMessage(x) //#define TraceMessage(i,x) { static local_enable = i; if (local_enable) USBExpertStatus(0, x, 0); local_enable = local_enable; } //#define TraceMessage(i,x) { static local_enable = i; if (local_enable) SysDebugStr(x); local_enable = local_enable; } #define TraceMessage(i,x) // masks for getting & setting serial config info #define kSerConfigBaudMask 0x03FF #define kSerConfigLenMask 0x0C00 #define kSerConfigParityMask 0x3000 #define kSerConfigStopMask 0xC000 ////////////////////////////////////////////////////////////////////////////////////// // // B_Prime // // this is the driver prime routine, called from the device manager in response to a _Read or _Write. // OSErr B_Read(ShimSerialGlobals * globals, IOCommandID ioCommandID, IOParam * pb) { #pragma unused (ioCommandID) OSErr err; TraceMessage(0,"\pEntering B_Read"); // initialize actual count read pb->ioActCount = 0; // make sure we've something to read or write err = (OSErr)((pb->ioReqCount) ? 1 : noErr); if ( err ) { // if we didn't find our globals, return readErr; if ( !globals ) return readErr; // attempt to fill read request immediately // from any pending data in the input buffer err = B_FillReadRequest(globals, pb); // ••• DO WE WANT TO KEEP DATA AFTER THE MODEM IS GONE? // if read request unsatisfied and we have no uart (card // was physically removed, etc.) then we better bail out // now with a readErr rather than leave the client hanging // TODO: What happens when our device disappears? // if ( !globals->uartRegs ) // return readErr; // otherwise if the request didn't complete, we set // this request as our new current read pb // if ( err > noErr ) // { // globals->pbIn = pb; // globals->pbInID = ioCommandID; // } // if software or hardware flow control are in effect // we prime input mechanism according to buffer levels // (otherwise, input is enabled and the pb will be // treated as input characters arrive from the uart) B_InputFlowControl(globals); } // return result to asm routine which will decide whether // to rts or jioDone... no, our logic does not support // immediate primes (neither does the scc serial driver) return err; } OSErr B_Write(ShimSerialGlobals * globals, IOCommandID ioCommandID, IOParam * pb) { #pragma unused (globals, ioCommandID) OSErr err; TraceMessage(0,"\pEntering B_Write"); // initialize actual count written pb->ioActCount = 0; // make sure we've something to read or write err = (OSErr)((pb->ioReqCount) ? 1 : noErr); if ( err ) { err = USBSerialWrite(pb); #ifdef NOTYET // if we didn't find our globals, return writErr; // -OR- // if we have no uart (card was physically removed, etc.) // then there's no use in trying, there won't be any transmit // buffer empty interrupts, so just return writErr now... if ((!globals) ) return writErr; // otherwise set this request as our current write pb // globals->pbOut = pb; // globals->pbOutID = ioCommandID; // turn on write buffer empty interrupts to prime our write mechanism B_EnableOutput(globals); #endif //NOTYET } // return result to asm routine which will decide whether // to rts or jioDone... no, our logic does not support // immediate primes (neither does the scc serial driver) return(err); } ////////////////////////////////////////////////////////////////////////////////////// // // B_SerReset // // serial driver reset control call. // OSErr B_SerReset(ShimSerialGlobals * globals, UInt16 config) { UInt16 baudRate; unsigned char len; unsigned char parity; unsigned char stop; TraceMessage(1,"\pEntering B_SerReset"); switch (config & kSerConfigBaudMask) { case baud300: baudRate = 300; break; case baud600: baudRate = 600; break; case baud1200: baudRate = 1200; break; case baud1800: baudRate = 1800; break; case baud2400: baudRate = 2400; break; case baud3600: baudRate = 3600; break; case baud4800: baudRate = 4800; break; case baud7200: baudRate = 7200; break; case baud9600: baudRate = 9600; break; case baud19200: baudRate = 19200; break; case baud38400: baudRate = 38400; break; case baud57600: baudRate = 57600; break; default: baudRate = 57600; break; } switch (config & kSerConfigLenMask) { case data5: len = kLen5Bits; break; case data6: len = kLen6Bits; break; case data7: len = kLen7Bits; break; case data8: len = kLen8Bits; break; default: len = kLen8Bits; break; } switch (config & kSerConfigParityMask) { case noParity: parity = kParNone; break; case oddParity: parity = kParOdd; break; case evenParity: parity = kParEven; break; default: parity = kParNone; break; } switch (config & kSerConfigStopMask) { case stop10: stop = kStop1Bit; break; case stop15: stop = kStop1_5Bits; break; case stop20: stop = kStop2Bits; break; default: stop = kStop1Bit; break; } // reset the baud rate on the chip B_SetBaudRate(globals, baudRate); // reset the other values globals->lenParStop = (UInt8)(len + parity + stop); B_SetLenParStop(globals, globals->lenParStop); return(noErr); } ////////////////////////////////////////////////////////////////////////////////////// // // B_SetBuffer // // select and initialize the serial input buffer. // // OSErr B_SetBuffer(ShimSerialGlobals * globals, Ptr newBuf, UInt16 bufLen) { OSErr err; long result; Boolean vmPresent; TraceMessage(1,"\pEntering B_SetBuffer"); // check for presence of virtual memory err = Gestalt(gestaltVMAttr,&result); vmPresent = ((err == noErr) && (result >= gestaltVMPresent)); // un-hold any previous input buffer if (vmPresent && globals->inBufPtr && (globals->inBufPtr != globals->builtInBuffer)) UnholdMemory(globals->inBufPtr,globals->inBufLen); // assign our local buffer as the default globals->inBufPtr = globals->builtInBuffer; globals->inBufLen = kBuiltInBufferSize; // reset buffer indexes globals->inBufStartIndex = 0; globals->inBufEndIndex = 0; // a non-zero buffer length means use client buffer // a zero buffer length means use the default buffer if (bufLen && newBuf) { // we need to make sure the buffer stays in physical memory and // doesn't get paged out to disk so our interrupt routines can // safely read and write to it if (vmPresent) { err = HoldMemory(newBuf,bufLen); if (err != noErr) return err; } // assign client buffer globals->inBufPtr = (UInt8 *) newBuf; globals->inBufLen = bufLen; } return(noErr); } ////////////////////////////////////////////////////////////////////////////////////// // // B_SetSerShk // // process the csSerHShake & csSerHShakeDTR control calls for input & output flow control. // // void B_SetSerShk(ShimSerialGlobals * globals, SerShk *shkNew, UInt16 csCode) { TraceMessage(1,"\pEntering B_SetSerShk"); // // set new cts value // globals->serShk.fCTS = shkNew->fCTS; // // copy over new xon / xoff characters // globals->serShk.xOn = shkNew->xOn; globals->serShk.xOff = shkNew->xOff; // // handle any change in software output flow control // if ( globals->serShk.fXOn != shkNew->fXOn ) { // // set new software output flow control value // if we are turning off output flow control then // we must re-enable output if it was suppressed // if ( !(globals->serShk.fXOn = shkNew->fXOn) ) B_EnableOutput(globals); } // copy over the errs & evts options fields // notice we do _nada_ with the events (no _PostEvent, etc.) globals->serShk.errs = shkNew->errs & (parityErr | hwOverrunErr | framingErr); globals->serShk.evts = shkNew->evts; // handle any change in software input flow control if ( globals->serShk.fInX != shkNew->fInX ) { // // assign new software input flow control value // if we are turning off input flow control then // we must send an xon _if_ an xoff has been sent // (if we are turning it on, it will activate on // the next character received) // if ( !(globals->serShk.fInX = shkNew->fInX) ) B_SendXOn(globals, false); } // if csCode is newer kSERDHandshake, // take account of the fDTR field if ( csCode == kSERDHandshake ) { // // handle any change in hardware input flow control // if ( globals->serShk.fDTR != shkNew->fDTR ) { // // assign new hardware input flow control value // if we are turning it on, go handle it now. // if turning it off, make sure to re-assert dtr! // globals->serShk.fDTR = shkNew->fDTR; if ( globals->serShk.fDTR ) B_InputFlowControl(globals); else B_EnableInput(globals, true); } } } ////////////////////////////////////////////////////////////////////////////////////// // // B_SendXOn // // void B_SendXOn(ShimSerialGlobals * globals, Boolean always) { TraceMessage(1,"\pEntering B_SendXOn"); if ( always || (globals->serStat.xOffSent & xOffWasSent) ) { globals->serStat.xOffSent &= (Byte)(~xOffWasSent); globals->xOnOffChar = globals->serShk.xOn; B_EnableOutput(globals); } } ////////////////////////////////////////////////////////////////////////////////////// // // B_SendXOff // // void B_SendXOff(ShimSerialGlobals * globals, Boolean always) { TraceMessage(1,"\pEntering B_SendXOff"); if ( always || !(globals->serStat.xOffSent & xOffWasSent)) { globals->serStat.xOffSent |= xOffWasSent; globals->xOnOffChar = globals->serShk.xOff; B_EnableOutput(globals); } } ////////////////////////////////////////////////////////////////////////////////////// // // B_SerStatus // // status routine fills in the SerStaRec record -- see inside mac vol. ii, serial drivers // // void B_SerStatus(ShimSerialGlobals * globals, SerStaRec *statRec) { TraceMessage(0,"\pEntering B_SerStatus"); // get & reset cumErrs statRec->cumErrs = globals->serStat.cumErrs; globals->serStat.cumErrs = 0; // has an xOff been sent to stop input? statRec->xOffSent = globals->serStat.xOffSent; // is there a pending read request? statRec->rdPend = (globals->pbIn != nil); // is there a pending write request? statRec->wrPend = (globals->pbOut != nil); // cts & xOff output flow control hold flags // TODO: return the state of CTSHold? statRec->ctsHold = true; // !(globals->uartRegs->MSR & kCTSCompliment); statRec->xOffHold = globals->serStat.xOffHold; } ////////////////////////////////////////////////////////////////////////////////////// // // B_SerGetBuf // // status routine computes & returns the number of bytes pending in the receive buffer // // UInt32 B_SerGetBuf(ShimSerialGlobals * globals) { SInt32 count; TraceMessage(0,"\pEntering B_SerGetBuf"); count = ((SInt32)globals->inBufEndIndex - (SInt32)globals->inBufStartIndex); if ( count < 0 ) count += globals->inBufLen; return count; } ////////////////////////////////////////////////////////////////////////////////////// // // B_EnableDTR // void B_EnableDTR(ShimSerialGlobals * globals, Boolean enable) { #pragma unused (globals) USBSetDTRState(enable); } ////////////////////////////////////////////////////////////////////////////////////// // // B_EnableRTS // void B_EnableRTS(ShimSerialGlobals * globals, Boolean enable) { #pragma unused (globals) USBSetRTSState(enable); } ////////////////////////////////////////////////////////////////////////////////////// // // B_SetBreak // void B_SetBreak(ShimSerialGlobals * globals, Boolean setBreak) { #pragma unused (globals) USBSetBreakState(setBreak); } ////////////////////////////////////////////////////////////////////////////////////// // // B_SetLenParStop // void B_SetLenParStop(ShimSerialGlobals * globals, UInt8 lenParStop ) { UInt8 parity; #pragma unused (globals) USBSetStopBits((lenParStop & kStop2Bits) ? 1 : 0); USBSetDataBits(lenParStop & 0x03); switch (lenParStop & 0x38){ case kParNone: parity = 0; break; case kParEven: parity = 1; break; case kParOdd: parity = 2; break; case kParLow: parity = 3; break; // I guessed -- is mark hi or low? case kParHigh: parity = 4; break; // I guessed -- is space hi or low? } USBSetParityBits(parity); } ////////////////////////////////////////////////////////////////////////////////////// // // B_SetBaudRate // // this is currently based upon a hardwired uart internal clock speed of 1.8432mhz // which appears to be industry standard. i guess that's what we'll use... // UInt16 B_SetBaudRate(ShimSerialGlobals * globals, UInt32 baudRate) { UInt16 divisor; // validate the requested baud rate if ( baudRate ) { if ( baudRate > kMaxBaudRate ) // ••• CHANGE FOR 115 & 230K Baud! baudRate = kMaxBaudRate; globals->baudRate = baudRate; } else baudRate = globals->baudRate; // compute the divisor value to pop into the divisor latch registers divisor = (UInt16)(globals->UARTCrystalSpeed / baudRate / 16); USBSetBaudRateDivisor(divisor); // return actual baud rate used return(globals->UARTCrystalSpeed / divisor / 16); } ////////////////////////////////////////////////////////////////////////////////////// // // B_SetParErrChar // // assigns parity error replacement character and alternate replacment character. // if peChar is zero then no parity error character substitution. // // void B_SetParErrChar(ShimSerialGlobals * globals, Boolean alt, char peChar, char peAltChar) { #pragma unused (globals, alt, peAltChar) USBSetParChar(peChar); #ifdef NOTYET // by default, we clear high bit on any received // character matching the replacement character globals->peAltChar = peChar & 0x7F; // if alternate call, assign the alternate character if ( alt ) globals->peAltChar = peAltChar; #endif //NOTYET } ////////////////////////////////////////////////////////////////////////////////////// // // B_FillReadRequest // // for filling _Read requests. attempts to copy requested bytes from internal serial // buffer into request parameter block. called from B_Read and B_UARTInput. // // OSErr B_FillReadRequest(ShimSerialGlobals * globals, IOParam * pb) { OSErr result; UInt16 endIndex; UInt16 startIndex; result = 1; while (result > noErr) { // this can be interrupted to add data to the internal buffer // so we update inBufStartIndex and inBufEndIndex each time // if there is nothing in the input buffer, leave quietly endIndex = globals->inBufEndIndex; startIndex = globals->inBufStartIndex; if ( startIndex == endIndex ) break; // copy byte into read request buffer and bump actual count pb->ioBuffer[pb->ioActCount] = globals->inBufPtr[startIndex]; pb->ioActCount++; // now update internal buffer inBufStartIndex startIndex++; if ( startIndex >= globals->inBufLen ) startIndex = 0; globals->inBufStartIndex = startIndex; // lastly, check if we've satisfied the request if ( pb->ioReqCount <= pb->ioActCount ) result = noErr; } return(result); } #ifdef NOTNEEDED ////////////////////////////////////////////////////////////////////////////////////// // // B_UARTInterrupt // // this routine handles incoming interrupts from the uart. // // InterruptMemberNumber B_UARTInterrupt(InterruptSetMember ISTmember, void *refCon, UInt32 theIntCount) { #pragma unused (ISTmember) #pragma unused (refCon) #pragma unused (theIntCount) UInt32 interruptIDMask; Boolean interruptHandled; TraceMessage(1,"\pEntering B_UARTInterrupt"); // Setup for spurious interrupt handling interruptHandled = false; // While there is an interrupt service it. do { // Get the value of the Interrupt Identification // Register and mask off the important bits interruptIDMask = (gGlobals->uartRegs->IIR) & kInterruptIDMask; // Since the bits of some values 'overlap' with others we must // check the value as a single unit, which is actually OK because // the UART will prioritize and only give us 1 interrupt at a time anyway. switch(interruptIDMask) { case kModemStatusInterrupt: B_UARTModemStatus(gGlobals); interruptHandled = true; break; case kTXEmptyInterrupt: B_UARTOutput(gGlobals); interruptHandled = true; break; case kRXDataInterrupt: case kRxStatusInterrupt: case kCharTimeoutInterrupt: B_UARTInput(gGlobals); interruptHandled = true; break; case kNoInterrupt: default: // We don't know what the interrupt was, or the UART // says we've serviced all pending interrupts...In // either case lets exit the interrupt handler because // we can no longer do any useful work. interruptIDMask = kNoInterrupt; break; } // Continue processing interrupts until the UART // gives us the 'no interrupts' pending signal }while(interruptIDMask != kNoInterrupt); return interruptHandled ? kIsrIsComplete : kIsrIsNotComplete; } #endif //NOTNEEDED ////////////////////////////////////////////////////////////////////////////////////// // // B_UARTModemStatus // // a modem status interrupt has been received. we treat it here. // // void B_UARTModemStatus(ShimSerialGlobals * globals) { unsigned char modemStatus; TraceMessage(1,"\pEntering B_UARTModemStatus"); modemStatus = USBGetModemStatus(); // // if cts has changed, set ctsHold accordingly -- note this value is really // silly and just to placate clients that want this kind of status info -- // we _always_ poll the modem status register to test cts during output // if (modemStatus & kCTSChanged) { globals->serStat.ctsHold = !(modemStatus & kCTSCompliment); B_EnableOutput(globals); } // // should handle ring indicate here // return; } #ifdef NOTNEEDED ////////////////////////////////////////////////////////////////////////////////////// // // B_UARTOutput // // a modem transmit buffer empty interrupt has been received. we treat it here. // // void B_UARTOutput(ShimSerialGlobals * globals) { IOParam * pb; volatile UART * uartRegs; Ptr bufPtr; OSErr result; SInt16 count; TraceMessage(1,"\pEntering B_UARTOutput"); // set return result to incomplete result = 1; // pull out reference to uartRegs // (address validated by caller B_UARTInterrupt) uartRegs = globals->uartRegs; // if we are using fifos we can blast up to 16 bytes at the uart count = (SInt16)((globals->useFIFO) ? 16 : 1); // first handle any pending xon or xoff char to send if ( globals->xOnOffChar ) { // write the xon or xoff character uartRegs->THR = globals->xOnOffChar; globals->xOnOffChar = 0; count--; } // pull out any pending write request pb = (IOParam *)globals->pbOut; if (pb) { // are there still bytes to write? if ( pb->ioActCount < pb->ioReqCount ) { // compute offset into the data buffer bufPtr = pb->ioBuffer + pb->ioActCount; // loop on fifo counter while (count > 0) { // Hardware output flow control check, if hardware flow control is // enabled and CTS is not asserted we must stop sending right now. if (gGlobals->serShk.fCTS && !(uartRegs->MSR & kCTSCompliment)) break; // write next byte into uart transmit register // update buffer pointer & actual counter uartRegs->THR = *bufPtr++; pb->ioActCount++; count--; // check if we have satisfied write request if ( pb->ioReqCount <= pb->ioActCount ) { // yes - set result and drop out of loop result = noErr; break; } } } // if we have finished this write request, we // clear our local pb field before completion // to avoid any misunderstandings... if ( result <= noErr ) globals->pbOut = nil; } // turn off write empty interrupts if we've no more data to send B_EnableOutput(globals); // clean up after ourselves if (result <= noErr) ShimSerialStubIODone((union ParamBlockRec *)pb,*(globals->dceOut),result); } ////////////////////////////////////////////////////////////////////////////////////// // // B_UARTInput // // this is the uart received data available interrupt service routine. we loop on rx data // ready bit set in the line status regiser, pulling the bytes from the receiver buffer // register. the rx data ready bit remains set as long as there are bytes to be read // (i.e. still data available in the uart receive fifo, which holds up to 16 bytes) // before returning check if there is an outstanding read and if so try to fill it. // // void B_UARTInput(ShimSerialGlobals * globals) { volatile UART * uartRegs; IOParam * pb; OSErr result; register UInt16 nextIndex; register unsigned char rcvByte; unsigned char lineStatus; unsigned char newErrs; unsigned char cumErrs; TraceMessage(0,"\pEntering B_UARTInput"); // pull out reference to uartRegs // (address validated by caller B_UARTInterrupt) uartRegs = globals->uartRegs; // this code will loop while there is data available in the uart receive buffer // it will also check for errors received via a kRxStatusInterrupt, since we // get called for kRxStatusInterrupt and kRXDataInterrupt. A kRxStatusInterrupt // is just treated like a kRXDataInterrupt with no outstanding data. // set return result to incomplete result = 1; cumErrs = 0; do { // pull out uart line status lineStatus = uartRegs->LSR; // handle reading of data available in the uart receive buffer if ( lineStatus & kRxDataAvail ) { // then snatch the input character from the uart receive register rcvByte = uartRegs->RBR; // if software output flow control is enabled, check if this is an xon or xoff character if ( globals->serShk.fXOn && (!(lineStatus & kRxErrMask)) && ( (rcvByte == globals->serShk.xOn) || (rcvByte == globals->serShk.xOff))) { // set software output flow control based on xon or xoff received globals->serStat.xOffHold = (rcvByte == globals->serShk.xOff); B_EnableOutput(globals); } else { // else this is to be treated as a data byte for the client // compute index into our input buffer nextIndex = globals->inBufEndIndex + 1; if ( nextIndex >= globals->inBufLen ) nextIndex = 0; // check for parity error replacement character if ( globals->peChar ) { // if parity error occured, replace the byte if ( lineStatus & kParityErr ) { rcvByte = globals->peChar; } else { // else if received byte matches replacement character, use alternate character if ( rcvByte == globals->peChar ) rcvByte = globals->peAltChar; } } // stuff the character into the input buffer and update the index globals->inBufPtr[globals->inBufEndIndex] = rcvByte; globals->inBufEndIndex = nextIndex; // check for input buffer overflow if ( globals->inBufEndIndex == globals->inBufStartIndex ) { // mark that we've had an overrun globals->serStat.cumErrs |= swOverrunErr; cumErrs |= swOverrunErr; // drop oldest character from our internal buffer globals->inBufStartIndex++; if ( globals->inBufStartIndex >= globals->inBufLen ) globals->inBufStartIndex = 0; // we've had a software overrun -- rather than empty the uart // fifo and overwrite up to 16 bytes, we quit now and let the // client get a chance to read in the existing bytes -- if there // are still bytes pending in the fifo, we'll get interrupted // again real soon now... } } } // check for receive error condition // for case of kRxStatusInterrupt with no data available if ( lineStatus & kRxErrMask ) { // mark error newErrs = 0; if ( lineStatus & kParityErr ) newErrs |= parityErr; if ( lineStatus & kOverrunErr ) newErrs |= hwOverrunErr; if ( lineStatus & kFramingErr ) newErrs |= framingErr; if ( lineStatus & kBreakDetect ) newErrs |= breakErr; globals->serStat.cumErrs |= newErrs; cumErrs |= newErrs; } // check for an outstanding read request pb = (IOParam *)globals->pbIn; if (pb) { // copy bytes from input buffer into pb read buffer // (result is either 1=incomplete or 0=complete) result = B_FillReadRequest(globals, pb); // handle errors that cause read aborts if ( cumErrs & globals->serShk.errs ) result = rcvrErr; // if we have fulfilled this read request, we // need to call IOCommandIsComplete and on it // and continue processing with the next request if ( result <= noErr ) { globals->pbIn = nil; break; } } // handle hardware (dtr or rts) or software // (xon / xoff) input flow control as necessary B_InputFlowControl(globals); // loop while there is data available in the uart receive buffer }while(!(cumErrs & swOverrunErr) && (lineStatus & kRxDataAvail)); // clean up after ourselves if (result <= noErr) ShimSerialStubIODone((union ParamBlockRec *)pb,*(globals->dceIn),result); } #endif //NOTNEEDED ////////////////////////////////////////////////////////////////////////////////////// // // B_EnableSerialDevice // // called once at driver open time to configure the uart, set baud rate, enable interrupts, etc. // // void B_EnableSerialDevice(ShimSerialGlobals * globals) { TraceMessage(1,"\pEntering B_EnableSerialDevice"); // TODO: setup the device? // Setup Interrupt transaction to look for status changes (and update status global) B_EnableRTS(globals, 1); B_EnableDTR(globals, 1); B_SetBaudRate(globals, 0); B_SetLenParStop(globals, globals->lenParStop); USBSetControl(0); // no flow control // do we have to fire off a chained read to keep the buffers full? USBStartReadPolling(); } ////////////////////////////////////////////////////////////////////////////////////// // // B_DisableSerialDevice // // called once when the driver is closed to turn off modem interrupts // // void B_DisableSerialDevice(ShimSerialGlobals * globals) { #pragma unused (globals) TraceMessage(1,"\pEntering B_DisableSerialDevice"); USBStopReadPolling(); B_EnableRTS(globals, 0); B_EnableDTR(globals, 0); } ////////////////////////////////////////////////////////////////////////////////////// // // B_InputFlowControl // // handles hardware (dtr or rts) and software (xon / xoff) input flow control // // void B_InputFlowControl(ShimSerialGlobals * globals) { UInt32 threshold; SInt32 count; TraceMessage(0,"\pEntering B_InputFlowControl"); // compute threshold for flow control -- same algorithm as h&j count = globals->inBufEndIndex - globals->inBufStartIndex; if (count < 0) count += globals->inBufLen; threshold = (4 * count) / globals->inBufLen; // turn it off... if (threshold >= 3) { // if hardware input flow control, turn it off now if (globals->serShk.fDTR && !(globals->serStat.xOffSent & dtrNegated)) B_EnableInput(globals, false); // if software (xon / xoff) input flow control // and we've sent an xoff if we haven't already if ( globals->serShk.fInX ) B_SendXOff(globals, false); } else { if (threshold < 1) { // ...or turn it on // if hardware input flow control, turn it back on now if (globals->serShk.fDTR && (globals->serStat.xOffSent & dtrNegated)) B_EnableInput(globals, true); // if software (xon / xoff) input flow control // and we've sent an xoff we need to send an xon if ( globals->serShk.fInX ) B_SendXOn(globals, false); } } } ////////////////////////////////////////////////////////////////////////////////////// // // B_EnableInput // // we use the rts enable bit on the modem control register for dtr // we also use a variant introduced by the h&j driver of setting this // value in xOffSent of the serStat record -- doesn't seem to cause // any problems and a convenient place to track changes to dtr (or rts) // // void B_EnableInput(ShimSerialGlobals * globals, Boolean enable) { TraceMessage(1,"\pEntering B_EnableInput"); if (enable) { #ifdef NOTYET globals->uartRegs->MCR |= kRTSEnable; #endif //NOTYET globals->serStat.xOffSent &= ~dtrNegated; } else { #ifdef NOTYET globals->uartRegs->MCR &= ~kRTSEnable; #endif //NOTYET globals->serStat.xOffSent |= dtrNegated; } } ////////////////////////////////////////////////////////////////////////////////////// // // B_EnableOutput // // this routine is the veritable on/off switch for transmission. whenever you have // something to send or whenever you've finished sending something or whenever you // get the itch, just call this baby and it will check the state of everything and // turn on or off the "transmit buffer empty" interrupts on the card's uart. when // those interrupts are enabled, our B_UARTOutput gets called, and we send, send, // send... until we can't send no more... then we call this baby again and // turn those nasty persistent interrupts off... clear as mud? // void B_EnableOutput(ShimSerialGlobals * globals) { #pragma unused (globals) TraceMessage(1,"\pEntering B_EnableOutput"); // TODO: ? #ifdef NOTYET // The basic theory here is to check for the best flow control mechanism and work // backwards to the no flow control case. If hardware flow control is enabled // and it says don't send, we will do nothing even if software flow control is also // enabled and it says send. However, if hardware flow control isn't enabled then // software flow control gets to set the policy, and lastly if no flow control is // enabled we just enable output if we have data to send. // if ( hardware flow control enabled _and_ ( output is not suppressed _and_ we have something to output )) // _or_ if ( no hardware and we have software _and_ ( we have an xon or xoff char to send _or_ we have a write request _and_ output is not suppressed )) // _or_ if ( no hardware and no software _and_ we have something to output) if ((gGlobals->serShk.fCTS && ((gGlobals->uartRegs->MSR & kCTSCompliment) && globals->pbOut)) || (!gGlobals->serShk.fCTS && globals->serShk.fXOn && (globals->xOnOffChar || (globals->pbOut && !globals->serStat.xOffHold))) || (!gGlobals->serShk.fCTS && !globals->serShk.fXOn && globals->pbOut)) { // ...then turn on transmit buffer empty interrupts... globals->uartRegs->IER |= kTXEmptyIntEnable; } else { // ...else turn them off globals->uartRegs->IER &= ~kTXEmptyIntEnable; } #endif //NOTYET }